package ch.sv7.tool.dbmanager.db.ant;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

import org.apache.tools.ant.BuildException;

import ch.sv7.tool.dbmanager.DbManagerException;
import ch.sv7.tool.dbmanager.StringUtil;
import ch.sv7.tool.dbmanager.db.DatabaseSchema;
import ch.sv7.tool.dbmanager.db.dialect.Dialect;
import ch.sv7.tool.dbmanager.db.dialect.DialectFactory;
import ch.sv7.tool.dbmanager.db.difference.SchemaDifferences;
import ch.sv7.tool.dbmanager.db.difference.filter.NameExcludeDifferenceFilter;
import ch.sv7.tool.dbmanager.db.xml.XmlTorqueDatabaseSchemaFactory;
import ch.sv7.tool.dbmanager.types.TypesInfo;
import ch.sv7.tool.dbmanager.types.TypesInfoFactory;
import ch.sv7.tool.log.AntAppender;
import ch.sv7.tool.log.Appender;
import ch.sv7.tool.log.Logger;

public class CompareTorqueSchemaTask extends TablesSqlTask {

	private File folder;
	private String includes;
	private String excludes;
	private String typeMapping;
	private File typeMappingFile;
	private String differencesExcludes;
	private boolean failOnError = true;
	private boolean failOnDifferences = true;
	private boolean xmlValidate = false;

	public CompareTorqueSchemaTask() {
		Logger.setAppender(new AntAppender(this, Appender.LEVEL_INFO));
	}

	public void execute() throws BuildException {
		try {
			java.sql.Connection conn = getConnection();
			try {
				log("Extracting Torque schema info");
				TypesInfo ti = TypesInfoFactory.getInstance().getTypesInfo(getDialect(), conn);
				Dialect d = DialectFactory.getInstance().getDialect(getDialect(), ti, getDialectProperties());
				
				XmlTorqueDatabaseSchemaFactory xmlFactory = 
				 	new XmlTorqueDatabaseSchemaFactory(
							folder, 
							includes, 
							excludes, 
							getSchema(),
							getTypeMappingProperties(),
							isXmlValidate(),
							d);
				DatabaseSchema xmlSchema = xmlFactory.getDatabaseSchema(xmlFactory.getTables());
				DatabaseSchema jdbcDbSchema = getDatabaseSchema(conn, getTables());
				
				SchemaDifferences differences = new SchemaDifferences();
				differences.addFilter(new NameExcludeDifferenceFilter(differencesExcludes));
				xmlSchema.computeDifferencesFromActual(differences, jdbcDbSchema);

				differences.dump(System.out);
				if (differences.hasBlockingDifferences())  {
					if (failOnDifferences){
						throw new BuildException("Incompatible database ! " + differences.getBlockingDifferencesSize() + " incompatibility(ies) found", getLocation());
					} else {
						log("There is schema difference between reference and actual DB ! " + differences.getBlockingDifferencesSize() + " difference(s) found");
					}					
				} else {
					log("Database is compatible with torque scripts !");
				}
				
			} catch (BuildException e) {
				log(e.getMessage());
				if (isFailOnError()){
					throw e;					
				}
			} catch (DbManagerException e) {
				log(e.getMessage());
				if (isFailOnError()){
					throw new BuildException(e, getLocation());			
				}			
			} catch (Exception e) {
				log(e.getMessage());
				if (isFailOnError()){
					throw new BuildException(e, getLocation());			
				}			
			} finally {
				closeConnection(conn);
			}
		} catch (DbManagerException e) {
			log(e.getMessage());
			if (isFailOnError()){
				throw new BuildException(e, getLocation());			
			}			
		}
	}

	public File getFolder() {
		return folder;
	}

	public void setFolder(File folder) {
		this.folder = folder;
	}

	
	/**
	 * @return the failOnError
	 */
	public boolean isFailOnError() {
		return failOnError;
	}

	/**
	 * @param failOnError the failOnError to set
	 */
	public void setFailOnError(boolean failOnError) {
		this.failOnError = failOnError;
	}


	/**
	 * @return the failOnDifferences
	 */
	public boolean isFailOnDifferences() {
		return failOnDifferences;
	}

	/**
	 * @param failOnDifferences the failOnDifferences to set
	 */
	public void setFailOnDifferences(boolean failOnDifferences) {
		this.failOnDifferences = failOnDifferences;
	}

	/**
	 * @return the differencesExcludes
	 */
	public String getDifferencesExcludes() {
		return differencesExcludes;
	}

	/**
	 * @param differencesExcludes the differencesExcludes to set
	 */
	public void setDifferencesExcludes(String differencesExcludes) {
		this.differencesExcludes = differencesExcludes;
	}

	/**
	 * @return the includes
	 */
	public String getIncludes() {
		return includes;
	}

	/**
	 * @param includes the includes to set
	 */
	public void setIncludes(String includes) {
		this.includes = includes;
	}

	/**
	 * @return the excludes
	 */
	public String getExcludes() {
		return excludes;
	}

	/**
	 * @param excludes the excludes to set
	 */
	public void setExcludes(String excludes) {
		this.excludes = excludes;
	}


	
	/**
	 * @return the xmlValidate
	 */
	public boolean isXmlValidate() {
		return xmlValidate;
	}

	/**
	 * @param xmlValidate the xmlValidate to set
	 */
	public void setXmlValidate(boolean xmlValidate) {
		this.xmlValidate = xmlValidate;
	}

	/**
	 * @return the typeMapping
	 */
	public String getTypeMapping() {
		return typeMapping;
	}

	/**
	 * @param typeMapping the typeMapping to set
	 */
	public void setTypeMapping(String typeMapping) {
		this.typeMapping = typeMapping;
	}

	/**
	 * @return the typeMappingFile
	 */
	public File getTypeMappingFile() {
		return typeMappingFile;
	}

	/**
	 * @param typeMappingFile the typeMappingFile to set
	 */
	public void setTypeMappingFile(File typeMappingFile) {
		this.typeMappingFile = typeMappingFile;
	}

	public Properties getTypeMappingProperties() {
		Properties mapping = new Properties();
		if (getTypeMappingFile() != null) {
			try {
				InputStream is = new FileInputStream(getTypeMappingFile());
				mapping.load(is);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else if (getTypeMapping() != null && getTypeMapping().trim().length() > 0){
			String[] mappings = StringUtil.commaSeparatedToArray(getTypeMapping());
			for (int i=0; i< mappings.length; i++) {
				int sep = mappings[i].indexOf("=");
				if (sep > 0){
					String originalType = mappings[i].substring(0, sep);
					String finalType = mappings[i].substring(sep + 1);
					mapping.put(originalType, finalType);
				}
			}
		}
		return mapping;
	}
}
